import { player, format } from './Synergism';
import { calculateSigmoidExponential, calculateSigmoid, calculateAnts, calculateRuneLevels, calculateAntSacrificeELO, calculateAntSacrificeRewards } from './Calculate';
import { Globals as G } from './Variables';

import type { DecimalSource } from 'break_infinity.js';
import Decimal from 'break_infinity.js';
import { achievementaward } from './Achievements';
import { Confirm, revealStuff } from './UpdateHTML';
import { updateTalismanInventory } from './Talismans';
import { buyResearch } from './Research';
import { resetAnts } from './Reset';
import type { ResetHistoryEntryAntSacrifice } from './History';
import { Synergism } from './Events';
import type { FirstToEighth, ZeroToSeven } from './types/Synergism';
import { DOMCacheGetOrSet } from './Cache/DOM';
import { smallestInc } from './Utility';
import { setInterval, clearInterval } from './Timers'

const antdesc: Record<string, string> = {
    antdesc1: 'Gain a Worker Ant for your everyday life. Gathers Galactic Crumbs. Essential!',
    antdesc2: 'Gain a Breeder Ant that produces Worker Ants automatically!',
    antdesc3: 'Gain a Meta-breeder Ant that produces Breeder Ants automatically!',
    antdesc4: 'Gain a Mega-breeder Ant that produces Meta-breeder Ants automatically!',
    antdesc5: 'Gain a Queen Ant that produces Mega-breeder Ants automatically!',
    antdesc6: 'Gain a Lord Royal Ant that produces Queen Ants automatically!',
    antdesc7: 'Gain an ALMIGHTY ANT that produces Lord Royal Ants automatically!',
    antdesc8: 'Gain a DISCIPLE OF ANT GOD that produces ALMIGHTY ANTS automatically!'
}

const antspecies: Record<string, string> = {
    antspecies1: 'Inceptus Formicidae',
    antspecies2: 'Fortunae Formicidae',
    antspecies3: 'Tributum Formicidae',
    antspecies4: 'Celeritas Formicidae',
    antspecies5: 'Multa Formicidae',
    antspecies6: 'Sacrificium Formicidae',
    antspecies7: 'Hic Formicidae',
    antspecies8: 'Experientia Formicidae',
    antspecies9: 'Praemoenio Formicidae',
    antspecies10: 'Scientia Formicidae',
    antspecies11: 'Phylacterium Formicidae',
    antspecies12: 'Mortuus Est Formicidae'
}

const antupgdesc: Record<string, string> = {
    antupgdesc1: 'Promotes romance and unity within the colony. [+12% Ant Speed / level]',
    antupgdesc2: 'Sweetens crumbs to increase their value [Each level increases Crumb --> Coin Conversion efficiency, up to ^50,000,000]',
    antupgdesc3: 'Swarms the Taxman into submission [Up to -99.5% taxes!]',
    antupgdesc4: 'Scares you into running faster [up to x20]',
    antupgdesc5: 'Imitates your body through magic shape-shifting powers [up to x40]',
    antupgdesc6: 'Tries to please Ant God... but fails [Additional Offerings!]',
    antupgdesc7: 'Helps you build a few things here and there [+3% Building Cost Delay / level, Cap 9,999,999%]',
    antupgdesc8: 'Knows how to salt and pepper food [Up to 1,000x Rune EXP!]',
    antupgdesc9: 'Can make your message to Ant God a little more clear [+1 all Rune Levels / level, Cap 10 Million]',
    antupgdesc10: 'Has big brain energy [Additional Obtainium!]',
    antupgdesc11: 'A valuable offering to the Ant God [Gain up to 3x Sacrifice Rewards!]',
    antupgdesc12: 'Betray Ant God increasing the fragility of your dimension [Unlocks ant talisman, Up to 2x faster timers on most things]'
}

export const calculateCrumbToCoinExp = () => {
    const exponent = player.currentChallenge.ascension !== 15
        ? 100000 + calculateSigmoidExponential(49900000, (player.antUpgrades[2-1]! + G['bonusant2']) / 5000 * 500 / 499)
        : 1/10000 * (100000 + calculateSigmoidExponential(49900000, (player.antUpgrades[2-1]! + G['bonusant2']) / 5000 * 500 / 499));

    return exponent
}

const antUpgradeTexts = [
    () => '所有蚂蚁工作速度变为' + format(Decimal.pow(1.12 + 1 / 1000 * player.researches[101], player.antUpgrades[1-1]! + G['bonusant1']), 2) + '倍',
    () => '碎屑对金币产量的加成效率变为^' + format(calculateCrumbToCoinExp()),
    () => '税收增长变为' + format(0.005 + 0.995 * Math.pow(0.99, player.antUpgrades[3-1]! + G['bonusant3']), 4) + '倍',
    () => '加速器加成增加' + format(100 * (calculateSigmoidExponential(20, (player.antUpgrades[4-1]! + G['bonusant4']) / 1000 * 20 / 19) - 1), 3) + '%',
    () => '加倍器增加' + format(100 * (calculateSigmoidExponential(40, (player.antUpgrades[5-1]! + G['bonusant5']) / 1000 * 40 / 39) - 1), 3) + '%',
    () => '祭品获取数量变为' + format(1 + Math.pow((player.antUpgrades[6-1]! + G['bonusant6']), 0.66), 4) + '倍',
    () => '建筑成本增长速度减缓' + format(Math.min(9999999,3 * player.antUpgrades[7-1]! + 3 * G['bonusant7']),0,true) + '%！',
    () => '符文经验值变为' + format(calculateSigmoidExponential(999, 1 / 10000 * Math.pow(player.antUpgrades[8-1]! + G['bonusant8'], 1.1)), 3) + '倍！',
    () => '每个符文有效等级增加' + format(1 * Math.min(1e7, (player.antUpgrades[9-1]! + G['bonusant9'])),0,true) + '',
    () => '难得素获取数量变为' + format(1 + 2 * Math.pow((player.antUpgrades[10-1]! + G['bonusant10']) / 50, 0.75), 4) + '倍',
    () => '献祭效果变为' + format(1 + 2 * (1 - Math.pow(2, -(player.antUpgrades[11-1]! + G['bonusant11']) / 125)), 4) + '倍',
    () => '全局速率变为' + format(calculateSigmoid(2, player.antUpgrades[12-1]! + G['bonusant12'], 69), 4) + '倍'
]

let repeatAnt: ReturnType<typeof setTimeout>;

export const antRepeat = (i: number) => {
    clearInterval(repeatAnt);
    repeatAnt = setInterval(() => updateAntDescription(i), 50);
}

export const updateAntDescription = (i: number) => {
    if (G['currentTab'] !== 'ants') {
        return
    }
    const el = DOMCacheGetOrSet('anttierdescription')
    const la = DOMCacheGetOrSet('antprice')
    const ti = DOMCacheGetOrSet('antquantity')
    const me = DOMCacheGetOrSet('generateant')

    let priceType = 'Galactic Crumbs'
    let tier: FirstToEighth = 'first'
    el.textContent = antdesc['antdesc' + i]

    switch (i) {
        case 1:
            priceType = 'Particles';
            tier = 'first';
            me.textContent = 'Generates ' + format(G['antOneProduce'], 5) + ' Crumbs/sec';
            break;
        case 2:
            tier = 'second';
            me.textContent = 'Generates ' + format(G['antTwoProduce'], 5) + ' Workers/sec';
            break;
        case 3:
            tier = 'third';
            me.textContent = 'Generates ' + format(G['antThreeProduce'], 5) + ' Breeders/sec';
            break;
        case 4:
            tier = 'fourth';
            me.textContent = 'Generates ' + format(G['antFourProduce'], 5) + ' MetaBreeders/sec';
            break;
        case 5:
            tier = 'fifth';
            me.textContent = 'Generates ' + format(G['antFiveProduce'], 5) + ' MegaBreeders/sec';
            break;
        case 6:
            tier = 'sixth';
            me.textContent = 'Generates ' + format(G['antSixProduce'], 5) + ' Queens/sec';
            break;
        case 7:
            tier = 'seventh';
            me.textContent = 'Generates ' + format(G['antSevenProduce'], 5) + ' Royals/sec';
            break;
        case 8:
            tier = 'eighth';
            me.textContent = 'Generates ' + format(G['antEightProduce'], 5) + ' ALMIGHTIES/sec';
            break;
    }
    la.textContent = 'Cost: ' + format(player[`${tier}CostAnts` as const]) + ' ' + priceType
    ti.textContent = 'Owned: ' + format(player[`${tier}OwnedAnts` as const]) + ' [+' + format(player[`${tier}GeneratedAnts` as const], 2) + ']'
}

const getAntCost = (originalCost: Decimal, buyTo: number, index: number) => {
    --buyTo

    //Determine how much the cost is for buyTo
    const cost = originalCost
        .times(Decimal.pow(G['antCostGrowth'][index-1], buyTo))
        .add(1 * buyTo);

    return cost;
}

const getAntUpgradeCost = (originalCost: Decimal, buyTo: number, index: number) => {
    --buyTo

    const cost = originalCost.times(Decimal.pow(G['antUpgradeCostIncreases'][index-1], buyTo));
    return cost;
}

//Note to self: REWRITE THIS SHIT Kevin :3
export const buyAntProducers = (pos: FirstToEighth, originalCost: DecimalSource, index: number) => {
    const sacrificeMult = antSacrificePointsToMultiplier(player.antSacrificePoints);
    //This is a fucking cool function. This will buymax ants cus why not

    //Things we need: the position of producers, the costvalues, and input var i
    originalCost = new Decimal(originalCost)
    //Initiate type of resource used
    const tag = index === 1 ? 'reincarnationPoints' : 'antPoints';
    const key = `${pos}OwnedAnts` as const;

    const buydefault = player[key] + smallestInc(player[key]);
    let buyTo = buydefault;
    let cashToBuy = getAntCost(originalCost, buyTo, index);
    while (player[tag].gte(cashToBuy)) {
        // Multiply by 4 until the desired amount. Iterate from there
        buyTo = buyTo * 4;
        cashToBuy = getAntCost(originalCost, buyTo, index);
    }
    let stepdown = Math.floor(buyTo / 8);
    while (stepdown >= smallestInc(buyTo)) {
        if (getAntCost(originalCost, buyTo - stepdown, index).lte(player[tag])) {
            stepdown = Math.floor(stepdown / 2);
        } else {
            buyTo = buyTo - Math.max(smallestInc(buyTo), stepdown);
        }
    }

    if (!player.antMax) {
        if (buydefault < buyTo) {
            buyTo = buydefault;
        }
    }
    // go down by 7 steps below the last one able to be bought and spend the cost of 25 up to the one that you started with and stop if coin goes below requirement
    let buyFrom = Math.max(buyTo - 6 - smallestInc(buyTo), buydefault);
    let thisCost = getAntCost(originalCost, buyFrom, index);
    while (buyFrom <= buyTo && player[tag].gte(thisCost)) {
        player[tag] = player[tag].sub(thisCost);
        player[key] = buyFrom;
        buyFrom = buyFrom + smallestInc(buyFrom);
        thisCost = getAntCost(originalCost, buyFrom, index);
        player[`${pos}CostAnts` as const] = thisCost;
    }
    if (player.reincarnationPoints.lt(0)) {
        player.reincarnationPoints = new Decimal('0')
    }
    if (player.antPoints.lt(0)) {
        player.antPoints = new Decimal('0')
    }
    calculateAntSacrificeELO();

    // Check if we award Achievement 176-182: Ant autobuy
    const achRequirements = [2, 6, 20, 100, 500, 6666, 77777];
    for (let j = 0; j < achRequirements.length; j++) {
        if (player.achievements[176 + j] === 0 && sacrificeMult > achRequirements[j] && player[`${G['ordinals'][j + 1 as ZeroToSeven]}OwnedAnts` as const] > 0) {
            achievementaward(176 + j)
        }
    }

    if (player.firstOwnedAnts > 6.9e7){
        player.firstOwnedAnts = 6.9e7
    }
}

export const buyAntUpgrade = (originalCost: DecimalSource, auto: boolean, index: number) => {
    if (player.currentChallenge.ascension !== 11) {
        originalCost = new Decimal(originalCost);
        const buydefault = player.antUpgrades[index-1]! + smallestInc(player.antUpgrades[index-1]!);
        let buyTo = buydefault;
        let cashToBuy = getAntUpgradeCost(originalCost, buyTo, index);
        while (player.antPoints.gte(cashToBuy)) {
            // Multiply by 4 until the desired amount. Iterate from there
            buyTo = buyTo * 4;
            cashToBuy = getAntUpgradeCost(originalCost, buyTo, index);
        }
        let stepdown = Math.floor(buyTo / 8);
        while (stepdown >= smallestInc(buyTo)) {
            if (getAntUpgradeCost(originalCost, buyTo - stepdown, index).lte(player.antPoints)) {
                stepdown = Math.floor(stepdown / 2);
            } else {
                buyTo = buyTo - Math.max(smallestInc(buyTo), stepdown);
            }
        }
        if (!player.antMax) {
            if (buydefault < buyTo) {
                buyTo = buydefault;
            }
        }
        // go down by 7 steps below the last one able to be bought and spend the cost of 25 up to the one that you started with and stop if coin goes below requirement
        let buyFrom = Math.max(buyTo - 6 - smallestInc(buyTo), buydefault);
        let thisCost = getAntUpgradeCost(originalCost, buyFrom, index);
        while (buyFrom <= buyTo && player.antPoints.gte(thisCost)) {
            player.antPoints = player.antPoints.sub(thisCost);
            player.antUpgrades[index-1] = buyFrom;
            buyFrom = buyFrom + smallestInc(buyFrom);
            thisCost = getAntUpgradeCost(originalCost, buyFrom, index);
        }
        calculateAnts();
        calculateRuneLevels();
        calculateAntSacrificeELO();
        if (!auto) {
            antUpgradeDescription(index)
        }
        if (player.antUpgrades[12-1] === 1 && index === 12) {
            revealStuff()
        }
    }
}

export const antUpgradeDescription = (i: number) => {
    const el = DOMCacheGetOrSet('antspecies')
    const al = DOMCacheGetOrSet('antlevelbonus');
    const la = DOMCacheGetOrSet('antupgradedescription')
    const ti = DOMCacheGetOrSet('antupgradecost')
    const me = DOMCacheGetOrSet('antupgradeeffect')

    const content1 = antspecies['antspecies' + i];
    const content2 = antupgdesc['antupgdesc' + i];
    const bonuslevel = G[`bonusant${i}` as keyof typeof G] as typeof G['bonusant1'];

    const c11 = player.currentChallenge.ascension === 11 ? 999 : 0;

    el.childNodes[0].textContent = content1 + ' Level ' + format(player.antUpgrades[i-1])
    al.textContent = ' [+' + format(Math.min(player.antUpgrades[i-1]! + c11, bonuslevel)) + ']'
    la.textContent = content2
    ti.textContent = 'Cost: ' + format(Decimal.pow(G['antUpgradeCostIncreases'][i-1], player.antUpgrades[i-1]! * G['extinctionMultiplier'][player.usedCorruptions[10]]).times(G['antUpgradeBaseCost'][i-1])) + ' Galactic Crumbs'
    me.textContent = '当前效果：' + antUpgradeTexts[i - 1]()
}

//function buyAntUpgrade(i,auto) {
//    if(player.antPoints.gte(Decimal.pow(10, G['antUpgradeCostIncreases'][i] * player.antUpgrades[i-1]).times(G['antUpgradeBaseCost'][i]))){
//        player.antPoints = player.antPoints.sub(Decimal.pow(10, G['antUpgradeCostIncreases'][i] * player.antUpgrades[i-1]).times(G['antUpgradeBaseCost'][i]));
//        player.antUpgrades[i-1]++
//        calculateAnts();
//        calculateRuneLevels();
//        calculateAntSacrificeELO();


//        if(!auto){antUpgradeDescription(i)}
//        if(player.antUpgrades[12-1] == 1 && i == 12){revealStuff()}
//    }
//    else{}
//}

export const antSacrificePointsToMultiplier = (points: number) => {
    let multiplier = Math.pow(1 + points / 5000, 2)
    multiplier *= (1 + 0.2 * Math.log(1 + points) / Math.log(10))
    if (player.achievements[174] > 0) {
        multiplier *= (1 + 0.4 * Math.log(1 + points) / Math.log(10))
    }
    return Math.min(1e300, multiplier);
}

export const showSacrifice = () => {
    const sacRewards = calculateAntSacrificeRewards();
    DOMCacheGetOrSet('antSacrificeSummary').style.display = 'block'

    DOMCacheGetOrSet('antELO').childNodes[0].textContent = 'Your Ant ELO is '
    DOMCacheGetOrSet('ELO').textContent = format(G['antELO'], 2)
    DOMCacheGetOrSet('effectiveELO').textContent = '[有效分' + format(G['effectiveELO'], 2, false) + ']'

    DOMCacheGetOrSet('antSacrificeMultiplier').childNodes[0].textContent = 'Ant Multiplier x' + format(antSacrificePointsToMultiplier(player.antSacrificePoints), 3, false) + ' --> '
    DOMCacheGetOrSet('SacrificeMultiplier').textContent = 'x' + format(antSacrificePointsToMultiplier(player.antSacrificePoints + sacRewards.antSacrificePoints), 3, false)

    DOMCacheGetOrSet('SacrificeUpgradeMultiplier').textContent = format(G['upgradeMultiplier'], 3, true) + 'x'
    DOMCacheGetOrSet('SacrificeTimeMultiplier').textContent = format(G['timeMultiplier'], 3, true) + 'x'
    DOMCacheGetOrSet('antSacrificeOffering').textContent = '+' + format(sacRewards.offerings)
    DOMCacheGetOrSet('antSacrificeObtainium').textContent = '+' + format(sacRewards.obtainium)
    if (player.challengecompletions[9] > 0) {
        DOMCacheGetOrSet('antSacrificeTalismanShard').textContent = '+' + format(sacRewards.talismanShards) + ' [分>500]'
        DOMCacheGetOrSet('antSacrificeCommonFragment').textContent = '+' + format(sacRewards.commonFragments) + ' [分>750]'
        DOMCacheGetOrSet('antSacrificeUncommonFragment').textContent = '+' + format(sacRewards.uncommonFragments) + ' [分>1,000]'
        DOMCacheGetOrSet('antSacrificeRareFragment').textContent = '+' + format(sacRewards.rareFragments) + ' [分>1,500]'
        DOMCacheGetOrSet('antSacrificeEpicFragment').textContent = '+' + format(sacRewards.epicFragments) + ' [分>2,000]'
        DOMCacheGetOrSet('antSacrificeLegendaryFragment').textContent = '+' + format(sacRewards.legendaryFragments) + ' [分>3,000]'
        DOMCacheGetOrSet('antSacrificeMythicalFragment').textContent = '+' + format(sacRewards.mythicalFragments) + ' [分>5,000]'
    }
}

export const sacrificeAnts = async (auto = false) => {
    let p = true

    if (player.antPoints.gte('1e40')) {
        if (!auto && player.toggles[32]) {
            p = await Confirm('This resets your Crumbs, Ants and Ant Upgrades in exchange for some multiplier and resources. Continue?')
        }
        if (p) {
            const antSacrificePointsBefore = player.antSacrificePoints;

            const sacRewards = calculateAntSacrificeRewards();
            player.antSacrificePoints += sacRewards.antSacrificePoints;
            player.runeshards += sacRewards.offerings;

            if (player.currentChallenge.ascension !== 14) {
                player.researchPoints += sacRewards.obtainium;
            }

            const historyEntry: ResetHistoryEntryAntSacrifice = {
                date: Date.now(),
                seconds: player.antSacrificeTimer,
                kind: 'antsacrifice',
                offerings: sacRewards.offerings,
                obtainium: sacRewards.obtainium,
                antSacrificePointsBefore: antSacrificePointsBefore,
                antSacrificePointsAfter: player.antSacrificePoints,
                baseELO: G['antELO'],
                effectiveELO: G['effectiveELO'],
                crumbs: player.antPoints.toString(),
                crumbsPerSecond: G['antOneProduce'].toString()
            };

            if (player.challengecompletions[9] > 0) {
                player.talismanShards += sacRewards.talismanShards;
                player.commonFragments += sacRewards.commonFragments;
                player.uncommonFragments += sacRewards.uncommonFragments;
                player.rareFragments += sacRewards.rareFragments;
                player.epicFragments += sacRewards.epicFragments;
                player.legendaryFragments += sacRewards.legendaryFragments;
                player.mythicalFragments += sacRewards.mythicalFragments;
            }

            // Now we're safe to reset the ants.
            resetAnts();
            player.antSacrificeTimer = 0;
            player.antSacrificeTimerReal = 0;
            updateTalismanInventory();
            if (player.autoResearch > 0 && player.autoResearchToggle) {
                const linGrowth = (player.autoResearch === 200) ? 0.01 : 0;
                buyResearch(player.autoResearch, true, linGrowth)
            }
            calculateAntSacrificeELO();

            Synergism.emit('historyAdd', 'ants', historyEntry);
        }
    }

    if (player.mythicalFragments >= 1e11 && player.currentChallenge.ascension === 14 && player.achievements[248] < 1) {
        achievementaward(248)
    }
}

export const autoBuyAnts = () => {
    const canAffordUpgrade = (x: number, m: DecimalSource) => player.antPoints.gte(getAntUpgradeCost(new Decimal(G['antUpgradeBaseCost'][x-1]), player.antUpgrades[x-1]! + 1, x).times(m))
    const ach = [176, 176, 177, 178, 178, 179, 180, 180, 181, 182, 182, 145];
    const cost = ['100', '100', '1000', '1000', '1e5', '1e6', '1e8', '1e11', '1e15', '1e20', '1e40', '1e100'];
    if (player.currentChallenge.ascension !== 11) {
        for (let i = 1; i <= ach.length; i++) {
            const check = i === 12 ? player.researches[ach[i - 1]] : player.achievements[ach[i - 1]];
            if (check && canAffordUpgrade(i, 2)) {
                buyAntUpgrade(cost[i - 1], true, i);
            }
        }
    }

    const _ach = [173, 176, 177, 178, 179, 180, 181, 182];
    const _cost = ['1e700', '3', '100', '10000', '1e12', '1e36', '1e100', '1e300'];
    for (let i = 1; i <= _ach.length; i++) {
        const res = i === 1 ? player.reincarnationPoints : player.antPoints;
        const m = i === 1 ? 1 : 2; // no multiplier on the first ant cost because it costs particles
        if (player.achievements[_ach[i - 1]] && res.gte(player[`${G['ordinals'][i - 1 as ZeroToSeven]}CostAnts` as const].times(m))) {
            buyAntProducers(
                G['ordinals'][i - 1] as Parameters<typeof buyAntProducers>[0],
                _cost[i - 1], i
            );
        }
    }
}
